using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetCorePal.Extensions.CodeAnalysis.SourceGenerators;

[Generator]
public class DomainEventHandlerMetadataGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var typeDeclarations = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax || node is RecordDeclarationSyntax,
                transform: (ctx, _) => ctx.Node)
            .Where(n => n is ClassDeclarationSyntax || n is RecordDeclarationSyntax);

        var compilationAndTypes = context.CompilationProvider.Combine(typeDeclarations.Collect());

        context.RegisterSourceOutput(compilationAndTypes, (spc, source) =>
        {
            var (compilation, typeNodes) = ((Compilation, System.Collections.Immutable.ImmutableArray<SyntaxNode>))source;
            var handlerMetas = new List<(string HandlerType, string EventType, List<string> CommandTypes)>();

            foreach (var typeDecl in typeNodes)
            {
                var semanticModel = compilation.GetSemanticModel(typeDecl.SyntaxTree);
                var symbol = semanticModel.GetDeclaredSymbol(typeDecl) as INamedTypeSymbol;
                if (symbol == null) continue;
                // 查找实现 IDomainEventHandler<TEvent> 或 IRequestHandler<TEvent> 的类型
                if (symbol.IsDomainEventHandler())
                {
                    // 获取领域事件类型
                    var eventTypeSymbol = symbol.GetDomainEventFromDomainEventHandler();
                    
                    if (eventTypeSymbol != null)
                    {
                        var eventType = eventTypeSymbol.ToDisplayString();
                        // 遍历所有公开方法，收集所有发出的命令类型
                        var commandTypes = new HashSet<string>();
                        foreach (var member in symbol.GetMembers().OfType<IMethodSymbol>())
                        {
                            var syntaxRef = member.DeclaringSyntaxReferences.FirstOrDefault();
                            if (syntaxRef == null) continue;
                            var methodSyntax = syntaxRef.GetSyntax() as MethodDeclarationSyntax;
                            if (methodSyntax == null) continue;
                            foreach (var cmdType in GeneratorExtensions.GetSentCommandTypes(methodSyntax, semanticModel))
                            {
                                commandTypes.Add(cmdType);
                            }
                        }
                        handlerMetas.Add((symbol.ToDisplayString(), eventType, commandTypes.ToList()));
                    }
                }
            }

            if (handlerMetas.Count > 0)
            {
                var sb = new StringBuilder();
                sb.AppendLine("// <auto-generated/>\nusing System;\nusing NetCorePal.Extensions.CodeAnalysis.Attributes;");
                foreach (var (handlerType, eventType, commandTypes) in handlerMetas)
                {
                    string commandsLiteral;
                    if (commandTypes.Count > 0)
                    {
                        commandsLiteral = "new string[] { " + string.Join(", ", commandTypes.Select(type => $"\"{type}\"")) + " }";
                    }
                    else
                    {
                        commandsLiteral = "new string[0]";
                    }
                    sb.AppendLine($"[assembly: DomainEventHandlerMetadataAttribute(\"{handlerType}\", \"{eventType}\", {commandsLiteral})]\n");
                }
                spc.AddSource("DomainEventHandlerMetadata.g.cs", sb.ToString());
            }
        });
    }
}
